/*
GameManager.cs
游戏行为管理器
*/

using System;
using System.Collections.Generic;
using full_leaf_framework.Interact;
using full_leaf_framework.Visual;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace touhou_bullet_rain;

public class GameManager {

    public Level level;

    public string playerName = "Anonymous Player";

    public static string uuid;
    public InputManager input;

    private Camera camera;
    private Camera staticCamera;

    private Drawable mouseDrawable;

    public bool isLevelActive = false;
    public bool isLevelVisible = false;

    public PlayerController playerController;
    public HudController menuController;

    public static BmfontController fontController;

    public int preMouseScroll = 0;
    public float cameraScaleDist = 1.0f;

    public static Queue<string> belowBoardcast;
    public float currBoardcastTime = 0f;

    public static ContentManager Content;
    public static SpriteBatch spriteBatch;

    public enum GameMode {
        Observer = 0,
        Player = 1,
        Disconnect = 2
    }
    public GameMode gameMode = GameMode.Observer;

#region 游戏信息初始化

    public GameManager() {
        input = new InputManager();
        input.InsertTrackingKeys(new Keys[6] { Keys.W, Keys.A, Keys.S, Keys.D, Keys.Space, Keys.Enter });
        camera = new Camera(spriteBatch,
            new Rectangle(0, 0, MainGame.SCREEN_WIDTH, MainGame.SCREEN_HEIGHT),
            new Vector2(0, 0),
            new Vector2(MainGame.SCREEN_WIDTH, MainGame.SCREEN_HEIGHT));
        staticCamera = new Camera(spriteBatch,
            new Rectangle(0, 0, MainGame.SCREEN_WIDTH, MainGame.SCREEN_HEIGHT),
            new Vector2(MainGame.SCREEN_WIDTH / 2, MainGame.SCREEN_HEIGHT / 2),
            new Vector2(MainGame.SCREEN_WIDTH, MainGame.SCREEN_HEIGHT));
        var mouseSprite = new AnimatedSprite(Content.Load<Texture2D>("mouse"));
        mouseDrawable = new Drawable(mouseSprite, Vector2.Zero, Vector2.Zero);
        menuController = new HudController(Content, staticCamera);
        fontController = new BmfontController("Font", "menu_font", Content);
        belowBoardcast = new Queue<string>();
        uuid = Guid.NewGuid().ToString();
        preMouseScroll = Mouse.GetState().ScrollWheelValue;
        camera.scale = 0.45f;
    }

    public async void StartLevel(string serverUri, string playerName) {
        level = new Level();
        isLevelActive = true;
        isLevelVisible = true;
        this.playerName = playerName;
        playerController = new PlayerController(level, null);
        await level.StartGame(serverUri + "/" + uuid);
    }
#endregion

#region 关卡更新信息

    public void Update(GameTime gameTime) {
        input.Update(gameTime);
        HandleLocalLevel();
        if (isLevelActive) {
            level.Update(gameTime);
            if (playerController is not null) {
                playerController.Update(gameTime, input, camera);
                if (playerController.ControlledPlayer is not null) {
                    CameraUpdate(gameTime);
                }
            }
            UpdateBelowBoardcast(gameTime);
        }
        menuController.Update(gameTime);
    }

    // 更新游戏下方的播报栏
    private void UpdateBelowBoardcast(GameTime gameTime) {
        if (belowBoardcast.Count > 0) {
            currBoardcastTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (currBoardcastTime > 4.0f) {
                currBoardcastTime = 0.0f;
                belowBoardcast.Dequeue();
            }
        }
        else {
            currBoardcastTime = 0.0f;
        }
    }

    // 更新游戏摄像机
    private void CameraUpdate(GameTime gameTime) {
        camera.cameraRange = new Vector2(MainGame.SCREEN_WIDTH, MainGame.SCREEN_HEIGHT);
        camera.drawRange = new Rectangle(0, 0, MainGame.SCREEN_WIDTH, MainGame.SCREEN_HEIGHT);
        Vector2 mouseActualPos = input.GetTrackingMouse().pos.ToVector2();
        Vector2 mouseOffset = mouseActualPos - new Vector2(MainGame.SCREEN_WIDTH / 2, MainGame.SCREEN_HEIGHT / 2);
        // 视角随鼠标移动
        const float sightOffsetRatio = 0.25f;
        Vector2 cameraDistPos = playerController.ControlledPlayer.pos + mouseOffset * sightOffsetRatio;
        float distance = Vector2.Distance(cameraDistPos, camera.pos);
        float approachVelocity = (distance * 1.2f + 0.15f * distance * distance + 15f) *
            (float)gameTime.ElapsedGameTime.TotalSeconds;
        if (approachVelocity < distance) {
            Vector2 swiftPos = cameraDistPos - camera.pos;
            swiftPos.Normalize();
            swiftPos *= approachVelocity;
            camera.pos += swiftPos;
        }
        else {
            camera.pos = cameraDistPos;
        }
        if (MathF.Abs(camera.scale - cameraScaleDist) < 0.01f) {
            camera.scale = cameraScaleDist;
        }
        else {
            camera.scale += camera.scale > cameraScaleDist ? -0.01f : 0.01f;
        }
        // 视角缩放操作
        if (Mouse.GetState().ScrollWheelValue > preMouseScroll) {
            cameraScaleDist = cameraScaleDist < 1.45f ? cameraScaleDist + 0.05f : 1.50f;
        }
        if (Mouse.GetState().ScrollWheelValue < preMouseScroll) {
            cameraScaleDist = cameraScaleDist > .65f ? cameraScaleDist - 0.05f : 0.60f;
        }
        preMouseScroll = Mouse.GetState().ScrollWheelValue;
    }

    // 处理本地的客户端更新
    public void HandleLocalLevel() {
        if (gameMode == GameMode.Observer && input.GetTrackingKey(Keys.Enter).fired) {
            gameMode = GameMode.Player;
            cameraScaleDist = 1.0f;
            playerController.RequestCreateNewPlayer(playerName, Vector2.Zero, uuid);
        }
        if (level.LevelClient.disconnected) {
            gameMode = GameMode.Disconnect;
        }
        if (playerController is not null && playerController.ControlledPlayer is not null) {
            Player targetPlayer = playerController.ControlledPlayer;
            Vector2 mousePos = camera.ReturnPointerPos(input.GetTrackingMouse().pos.ToVector2());
            if (Vector2.Distance(targetPlayer.pos, mousePos) > float.Epsilon) {
                targetPlayer.angle = Player.VectorToAngle(mousePos - targetPlayer.pos);
            }
            else {
                targetPlayer.angle = 0.0f;
            }
        }
    }
#endregion

#region 绘制相关游戏信息

    public void Draw() {
        if (isLevelActive && isLevelVisible) { level.Draw(camera); }
        mouseDrawable.pos = input.GetTrackingMouse().pos.ToVector2();
        mouseDrawable.angle += MathF.PI / 180;
        staticCamera.insertObject(mouseDrawable);
        // 绘制鼠标信息
        menuController.Draw();
        // 绘制菜单栏
        camera.Draw();
        DrawPlayerInfo();
        DrawLocalInfo();
        DrawRankInfo();
        DrawLogInfo();
        DrawNetworkInfo();
        DrawBelowBoardcast();
        staticCamera.Draw();
        // 绘制其他信息
    }

    private void DrawBelowBoardcast() {
        if (belowBoardcast.Count > 0) {
            // 前0.5f跳出，后0.5f收回
            float currPosY = MainGame.SCREEN_HEIGHT;
            if (currBoardcastTime <= 0.5f) {
                currPosY -= 210.0f * MathF.Sin(currBoardcastTime / 0.5f * MathF.PI / 2);
            }
            else if (currBoardcastTime >= 3.5f) {
                currPosY -= 210.0f * MathF.Sin((4f - currBoardcastTime) / 0.5f * MathF.PI / 2);
            }
            else {
                currPosY -= 210.0f;
            }
            fontController.InsertDrawObjects(staticCamera, belowBoardcast.Peek(), new Vector2(MainGame.SCREEN_WIDTH / 2, currPosY),
                BmfontDrawable.TranslateMethod.Middle, 2f);
        }
    }

    private void DrawPlayerInfo() {
        if (gameMode == GameMode.Player && playerController is not null
        && playerController.ControlledPlayer is not null) {
            Player player = playerController.ControlledPlayer;
            fontController.InsertDrawObjects(staticCamera, player.playerName, new Vector2(40, MainGame.SCREEN_HEIGHT - 70),
            BmfontDrawable.TranslateMethod.Left, 1f);
            if (player.immuneTime >= float.Epsilon) {
                fontController.InsertDrawObjects(staticCamera, "Immune Time " + MathF.Round(player.immuneTime, 1).ToString(), new Vector2(40, MainGame.SCREEN_HEIGHT - 35),
                BmfontDrawable.TranslateMethod.Left, 2f);
            }
            else if (player.hitPoints >= float.Epsilon) {
                fontController.InsertDrawObjects(staticCamera, "HitPoints " + player.hitPoints.ToString(), new Vector2(40, MainGame.SCREEN_HEIGHT - 35),
                BmfontDrawable.TranslateMethod.Left, 2f);
            }
            else {
                fontController.InsertDrawObjects(staticCamera, "Recovery " + MathF.Round(player.recovery, 1).ToString(), new Vector2(40, MainGame.SCREEN_HEIGHT - 35),
                BmfontDrawable.TranslateMethod.Left, 2f);
            }
            // 状态信息
            string weaponName = "Report this to developer!";
            if (Projectile.bulletDatas.ContainsKey(player.weapon.bulletType)) {
                weaponName = Projectile.bulletDatas[player.weapon.bulletType].WeaponName;
            }
            fontController.InsertDrawObjects(staticCamera, weaponName,
                new Vector2(MainGame.SCREEN_WIDTH - 40, MainGame.SCREEN_HEIGHT - 110),
                BmfontDrawable.TranslateMethod.Right, 2f);
            fontController.InsertDrawObjects(staticCamera, player.weapon.ammo.ToString(),
                new Vector2(MainGame.SCREEN_WIDTH - 60, MainGame.SCREEN_HEIGHT - 70),
                BmfontDrawable.TranslateMethod.Right, 2f);
            fontController.InsertDrawObjects(staticCamera, "/" + player.weapon.maxAmmo,
                new Vector2(MainGame.SCREEN_WIDTH - 45, MainGame.SCREEN_HEIGHT - 25),
                BmfontDrawable.TranslateMethod.Left, 1f);
            // 武器信息
            if (level.gameStarted) {
                fontController.InsertDrawObjects(staticCamera, ">  " + (int)level.leftTime + "  <", new Vector2(MainGame.SCREEN_WIDTH / 2, 30),
                    BmfontDrawable.TranslateMethod.Middle, 2f);
            }
            else if (level.autoStart) {
                if (level.Players.Count < level.autoStartMinPlayerCount) {
                    int leftPlayers = level.autoStartMinPlayerCount - level.Players.Count;
                    fontController.InsertDrawObjects(staticCamera, "Still Waiting for " + leftPlayers + " Player(s) to Start", new Vector2(MainGame.SCREEN_WIDTH / 2, 45),
                    BmfontDrawable.TranslateMethod.Middle, 2f);
                }
                else {
                    if (level.autoStartTime >= float.Epsilon)
                        fontController.InsertDrawObjects(staticCamera, "Game Auto Start in " + MathF.Round(level.autoStartTime, 0) + " Second(s)", new Vector2(MainGame.SCREEN_WIDTH / 2, 45),
                        BmfontDrawable.TranslateMethod.Middle, 2f);
                    else {
                        fontController.InsertDrawObjects(staticCamera, "Starting...", new Vector2(MainGame.SCREEN_WIDTH / 2, 45),
                        BmfontDrawable.TranslateMethod.Middle, 2f);
                    }
                }
            }
            else {
                fontController.InsertDrawObjects(staticCamera, "Waiting for the Host to Start", new Vector2(MainGame.SCREEN_WIDTH / 2, 45),
                BmfontDrawable.TranslateMethod.Middle, 2f);
            }
            // 对局信息
            fontController.InsertDrawObjects(staticCamera, "Score", new Vector2(30, 30),
                BmfontDrawable.TranslateMethod.Left, 1f);
            fontController.InsertDrawObjects(staticCamera, player.score.ToString() + " pts", new Vector2(30, 60),
                BmfontDrawable.TranslateMethod.Left, 2f);
            fontController.InsertDrawObjects(staticCamera, "Kill / Death", new Vector2(30, 110),
                BmfontDrawable.TranslateMethod.Left, 1f);
            fontController.InsertDrawObjects(staticCamera, player.killCount.ToString() + " / " +
                player.deathCount.ToString(), new Vector2(30, 140),
                BmfontDrawable.TranslateMethod.Left, 2f);
            // 玩家分数与击杀信息
        }
    }

    private void DrawLocalInfo() {
        if (gameMode == GameMode.Observer) {
            fontController.InsertDrawObjects(staticCamera, "Observer Mode", new Vector2(MainGame.SCREEN_WIDTH / 2, MainGame.SCREEN_HEIGHT - 100),
                BmfontDrawable.TranslateMethod.Middle, 2f);
            fontController.InsertDrawObjects(staticCamera, "Hold Enter to Join Server", new Vector2(MainGame.SCREEN_WIDTH / 2, MainGame.SCREEN_HEIGHT - 65),
                BmfontDrawable.TranslateMethod.Middle, 1f);
            if (level.serverName.Length > 0) {
                fontController.InsertDrawObjects(staticCamera, level.serverName, new Vector2(MainGame.SCREEN_WIDTH / 2, 70),
                    BmfontDrawable.TranslateMethod.Middle, 2f);
                fontController.InsertDrawObjects(staticCamera, level.serverInfo, new Vector2(MainGame.SCREEN_WIDTH / 2, 120),
                    BmfontDrawable.TranslateMethod.Middle, 1f);
            }
            fontController.InsertDrawObjects(staticCamera, "Local Uuid -> " + uuid, new Vector2(20, MainGame.SCREEN_HEIGHT - 20),
                BmfontDrawable.TranslateMethod.Left, 1f);
        }
        if (gameMode == GameMode.Disconnect) {
            fontController.InsertDrawObjects(staticCamera, "Disconnected from Server", new Vector2(MainGame.SCREEN_WIDTH / 2, MainGame.SCREEN_HEIGHT - 100),
                BmfontDrawable.TranslateMethod.Middle, 2f);
            fontController.InsertDrawObjects(staticCamera, "Server is Closed or your Internet Connection is too Weak",
                new Vector2(MainGame.SCREEN_WIDTH / 2, MainGame.SCREEN_HEIGHT - 65), BmfontDrawable.TranslateMethod.Middle, 1f);
            fontController.InsertDrawObjects(staticCamera, "Local Uuid -> " + uuid, new Vector2(20, MainGame.SCREEN_HEIGHT - 20),
                BmfontDrawable.TranslateMethod.Left, 1f);
        }
    }

    private void DrawRankInfo() {
        PriorityQueue<Player, int> playerRank = new PriorityQueue<Player, int>();
        foreach (var player in level.Players.Values) {
            playerRank.Enqueue(player, -player.score);
        }
        // 其实更新并不需要每一回合都做，之后优化一下这部分
        fontController.InsertDrawObjects(staticCamera, "Rank", new Vector2(MainGame.SCREEN_WIDTH - 50, 35),
            BmfontDrawable.TranslateMethod.Right, 1f);
        if (level.Players.Count == 0) {
        fontController.InsertDrawObjects(staticCamera, "No Player", new Vector2(MainGame.SCREEN_WIDTH - 100, 60),
            BmfontDrawable.TranslateMethod.Right, 1f);
        }
        else {
            int rank = 1;
            int currPosY = 60;
            const int swiftY = 90;
            while (playerRank.Count > 0) {
                fontController.InsertDrawObjects(staticCamera, rank + ". " + playerRank.Peek().playerName, new Vector2(MainGame.SCREEN_WIDTH - 150, currPosY),
                    BmfontDrawable.TranslateMethod.Right, 1f);
                fontController.InsertDrawObjects(staticCamera, playerRank.Peek().score + " pts", new Vector2(MainGame.SCREEN_WIDTH - 105, currPosY + 24),
                    BmfontDrawable.TranslateMethod.Left, 1f);
                fontController.InsertDrawObjects(staticCamera, "K / D : " + playerRank.Peek().killCount + " / " + playerRank.Peek().deathCount,
                    new Vector2(MainGame.SCREEN_WIDTH - 120, currPosY + 50),
                    BmfontDrawable.TranslateMethod.Middle, 1f);
                currPosY += swiftY;
                rank++;
                playerRank.Dequeue();
            }
        }
    }

    private void DrawLogInfo() {
        string[] logs = level.logs.ToArray();
        fontController.InsertDrawObjects(staticCamera, logs,
            new Vector2(10, 185), BmfontDrawable.TranslateMethod.Left, 1f);
    }

    private void DrawNetworkInfo() {
        if (!(gameMode == GameMode.Player)) { return; }
        long latency = level.latency;
        fontController.InsertDrawObjects(staticCamera, "Ping: " + latency + "ms",
            new Vector2(MainGame.SCREEN_WIDTH / 2, MainGame.SCREEN_HEIGHT - 15), BmfontDrawable.TranslateMethod.Left, 0.5f);
    }

}
#endregion